package com.yunkss.eftool.crypto.operator;

import com.yunkss.eftool.crypto.key.CisCodec;
import com.yunkss.eftool.crypto.key.SM4KeyGenerator;
import com.yunkss.eftool.crypto.params.CisAlgorithm;
import com.yunkss.eftool.crypto.params.SmProvider;
import org.springframework.stereotype.Component;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.security.*;

/**
 * @author csx
 * @date 2022/2/18 8:47
 * @desc
 */

@Component
public class EftoolSM4Operator extends SmProvider {
    private static final byte[] DEFAULT_IV = new byte[16];

    public EftoolSM4Operator() {
    }

    public String encryptECB(String hexKey, String plainText) throws NoSuchPaddingException, NoSuchAlgorithmException, IllegalBlockSizeException, BadPaddingException, NoSuchProviderException, InvalidKeyException {
        if (hexKey != null && plainText != null) {
            return CisCodec.encodeToHexString(this.encryptECB(CisCodec.decodeFromHexString(hexKey), plainText.getBytes(), CisAlgorithm.ALGORITHM_SM4_ECB_PADDING));
        } else {
            throw new NullPointerException();
        }
    }

    public String decryptECB(String hexKey, String cipherHexText) throws NoSuchPaddingException, NoSuchAlgorithmException, IllegalBlockSizeException, BadPaddingException, NoSuchProviderException, InvalidKeyException {
        if (hexKey != null && cipherHexText != null) {
            byte[] decryptECB = this.decryptECB(CisCodec.decodeFromHexString(hexKey), CisCodec.decodeFromHexString(cipherHexText), CisAlgorithm.ALGORITHM_SM4_ECB_PADDING);
            return new String(decryptECB);
        } else {
            throw new NullPointerException();
        }
    }

    private byte[] encryptECB(byte[] key, byte[] plainText, CisAlgorithm algorithm) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, NoSuchProviderException, BadPaddingException, IllegalBlockSizeException {
        Cipher ecbCipher = this.getECBCipher(algorithm, EftoolSM4Operator.SM4Mode.ENCRYPT, key);
        return ecbCipher.doFinal(plainText);
    }

    private byte[] decryptECB(byte[] key, byte[] cipherText, CisAlgorithm algorithm) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, NoSuchProviderException, BadPaddingException, IllegalBlockSizeException {
        Cipher ecbCipher = this.getECBCipher(algorithm, EftoolSM4Operator.SM4Mode.DECRYPT, key);
        return ecbCipher.doFinal(cipherText);
    }

    public String encryptCBC(String hexKey, String plainText) throws NoSuchPaddingException, InvalidKeyException, NoSuchAlgorithmException, IllegalBlockSizeException, BadPaddingException, NoSuchProviderException, InvalidAlgorithmParameterException {
        if (hexKey != null && plainText != null) {
            byte[] bytes = this.encryptCBC(CisCodec.decodeFromHexString(hexKey), DEFAULT_IV, plainText.getBytes(), CisAlgorithm.ALGORITHM_SM4_CBC_PADDING);
            return CisCodec.encodeToHexString(bytes);
        } else {
            throw new NullPointerException();
        }
    }

    public String encryptCBC(String hexKey, String hexIV, String plainText) throws NoSuchPaddingException, InvalidKeyException, NoSuchAlgorithmException, IllegalBlockSizeException, BadPaddingException, NoSuchProviderException, InvalidAlgorithmParameterException {
        if (hexKey != null && plainText != null) {
            byte[] bytes = this.encryptCBC(CisCodec.decodeFromHexString(hexKey), CisCodec.decodeFromHexString(hexIV), plainText.getBytes(), CisAlgorithm.ALGORITHM_SM4_CBC_PADDING);
            return CisCodec.encodeToHexString(bytes);
        } else {
            throw new NullPointerException();
        }
    }

    private byte[] encryptCBC(byte[] key, byte[] iv, byte[] plaintText, CisAlgorithm algorithm) throws NoSuchAlgorithmException, NoSuchPaddingException, NoSuchProviderException, InvalidAlgorithmParameterException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException {
        Cipher cbcCipher = this.getCBCCipher(algorithm, EftoolSM4Operator.SM4Mode.ENCRYPT, key, iv);
        return cbcCipher.doFinal(plaintText);
    }

    public String decryptCBC(String hexKey, String cipherHexText) throws NoSuchPaddingException, InvalidKeyException, NoSuchAlgorithmException, IllegalBlockSizeException, BadPaddingException, NoSuchProviderException, InvalidAlgorithmParameterException {
        if (hexKey != null && cipherHexText != null) {
            byte[] decryptCBC = this.decryptCBC(CisCodec.decodeFromHexString(hexKey), DEFAULT_IV, CisCodec.decodeFromHexString(cipherHexText), CisAlgorithm.ALGORITHM_SM4_CBC_PADDING);
            return new String(decryptCBC);
        } else {
            throw new NullPointerException();
        }
    }

    public String decryptCBC(String hexKey, String hexVI, String cipherHexText) throws NoSuchPaddingException, InvalidKeyException, NoSuchAlgorithmException, IllegalBlockSizeException, BadPaddingException, NoSuchProviderException, InvalidAlgorithmParameterException {
        if (hexKey != null && cipherHexText != null) {
            byte[] decryptCBC = this.decryptCBC(CisCodec.decodeFromHexString(hexKey), CisCodec.decodeFromHexString(hexVI), CisCodec.decodeFromHexString(cipherHexText), CisAlgorithm.ALGORITHM_SM4_CBC_PADDING);
            return new String(decryptCBC);
        } else {
            throw new NullPointerException();
        }
    }

    private byte[] decryptCBC(byte[] key, byte[] iv, byte[] cipherText, CisAlgorithm algorithm) throws NoSuchAlgorithmException, NoSuchPaddingException, NoSuchProviderException, InvalidAlgorithmParameterException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException {
        Cipher cbcCipher = this.getCBCCipher(algorithm, EftoolSM4Operator.SM4Mode.DECRYPT, key, iv);
        return cbcCipher.doFinal(cipherText);
    }

    private Cipher getECBCipher(CisAlgorithm algorithmName, EftoolSM4Operator.SM4Mode mode, byte[] key) throws NoSuchAlgorithmException, NoSuchProviderException, NoSuchPaddingException, InvalidKeyException {
        Cipher cipher = Cipher.getInstance(algorithmName.getName(), "BC");
        Key sm4Key = new SecretKeySpec(key, CisAlgorithm.ALGORITHM_SM4_FOR_NAME.getName());
        cipher.init(mode.getCode(), sm4Key);
        return cipher;
    }

    private Cipher getCBCCipher(CisAlgorithm algorithmName, EftoolSM4Operator.SM4Mode mode, byte[] key, byte[] iv) throws InvalidKeyException, InvalidAlgorithmParameterException, NoSuchAlgorithmException, NoSuchProviderException, NoSuchPaddingException {
        Cipher cipher = Cipher.getInstance(algorithmName.getName(), "BC");
        Key sm4Key = new SecretKeySpec(key, CisAlgorithm.ALGORITHM_SM4_FOR_NAME.getName());
        IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
        cipher.init(mode.getCode(), sm4Key, ivParameterSpec);
        return cipher;
    }

    public static String getDefaultIVHex() {
        return CisCodec.encodeToHexString(DEFAULT_IV);
    }

    static {
        System.arraycopy("ec9ca548d94452f0486367cc9ce68a29".getBytes(), 0, DEFAULT_IV, 0, 16);
    }

    protected static enum SM4Mode {
        ENCRYPT(1),
        DECRYPT(2);

        int code;

        private SM4Mode(int code) {
            this.code = code;
        }

        public int getCode() {
            return this.code;
        }
    }

    public static void main(String[] args) throws NoSuchAlgorithmException, NoSuchProviderException, InvalidAlgorithmParameterException, NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException, InvalidKeyException {
        String key = SM4KeyGenerator.generateSM4Key();
        EftoolSM4Operator sm4 = new EftoolSM4Operator();

        String sss = sm4.encryptCBC("59a9fb76daa68c0a69635c1a196d2ab4", "772d6bebb0e7e24a1c549aee7312b9d7", "崔树雄测试");

        System.out.println(sss);

        String aa = sm4.decryptCBC("59a9fb76daa68c0a69635c1a196d2ab4", "772d6bebb0e7e24a1c549aee7312b9d7", sss);

        System.out.println(aa);
//        sm4.g
    }

}